import { _decorator, Camera, Component, DirectionalLight, Node, Vec3 } from 'cc';
import { CameraMoveType, distanceLength, WatchViewType } from '../core/CommUtil';
import { EventManager } from '../core/EventManager';
import { BilliardGame } from './BilliardGame';
import { Vector3 } from '../framework/Vector3';
import { TableAtrribute } from '../core/TableAttribute';
import { Databus } from '../core/Databus';
const { ccclass, property } = _decorator;

const databus = Databus.Instance();

@ccclass('MainCameraController')
export class MainCameraController extends Component {
  public mainDirectLight: DirectionalLight = null;
  public smoothMoveRate: number = 0.01;
  public oriCameraPos = new Vec3(0, 45, 0);

  public cameraType: CameraMoveType = 0;
  public initCameraPos = new Vec3(0, 45, 0);
  public aimLookPoint: Vec3 = new Vec3(0, 0, 0);
  public ts: number = 0;
  public cameraNode = null;
  public cameraFov = 46;
  // 跟踪目标移动
  public targetObject: any = new Vec3(0, 0, 0);
  // 移动视角
  public fromPosition: Vec3 = new Vec3(0, 0, 0);
  public fromLookPosition: Vec3 = new Vec3(0, 0, 0);
  public targetPosition: Vec3 = new Vec3(0, 0, 0);
  public targetLookPosition: Vec3 = null;

  // 折射线方向，白球位置 + 折射方向
  public virtualBallPosition: Vec3 = new Vec3(0, 0, 0);
  public refractiveLine: Vec3 = new Vec3(0, 0, 0);
  onLoad() {
    this.mainDirectLight = this.node.getParent().getChildByName('Main Light').getComponent(DirectionalLight);
    this.cameraType = CameraMoveType.SmoothMoveTo;

    this.cameraNode = this.node.getComponent(Camera);
    EventManager.Instance().AddEventListener('CameraFollowTargetMove', this, this.updateFollowTarget);
    EventManager.Instance().AddEventListener('CameraAimPointDirection', this, this.updateAimPointDirection);
    EventManager.Instance().AddEventListener('CameraSmoothMoveDirection', this, this.updateSmoothMoveDirection);
    EventManager.Instance().AddEventListener('CameraRestart3DView', this, this.restart3DView);
    EventManager.Instance().AddEventListener('CameraRestartGame', this, this.restartGameView);
    EventManager.Instance().AddEventListener('ChangeCameraHeight', this, this.updateHeight);
    // 测试点击使用
    EventManager.Instance().AddEventListener('CameraFixedPoint', this, this.updateFixedPoint);
  }
  start() {
    // 适配相机的高度来调整桌面的大小
    if (databus.sceenSize.isSmallerWidth()) {
      this.oriCameraPos.y = 47.5;
      this.initCameraPos.y = 47.5;
    }
  }
  restartGameView() {
    this.cameraType = CameraMoveType.StartGameScene;
    this.fromPosition = new Vec3(60, 40, -60);
    this.fromLookPosition = new Vec3(0, TableAtrribute.DeskTopAxisY, 25);

    this.ts = 0;
    this.targetPosition = new Vec3(28, TableAtrribute.DeskTopAxisY + 6, 0);
    this.targetLookPosition = new Vec3(0, TableAtrribute.DeskTopAxisY, this.oriCameraPos.z);
  }

  restart3DView() {
    this.cameraType = CameraMoveType.AimPointDirection;
  }
  updateHeight(name: string, diff: number) {
    this.initCameraPos.y += diff;
    if (this.initCameraPos.y < TableAtrribute.DeskTopAxisY) {
      this.initCameraPos.y = TableAtrribute.DeskTopAxisY;
    }
    if (this.initCameraPos.y > 2 * TableAtrribute.DeskTopAxisY) {
      this.initCameraPos.y = 2 * TableAtrribute.DeskTopAxisY;
    }
  }
  update(deltaTime: number) {
    this.ts += deltaTime;
    // 2D 视角 垂直俯视
    if (BilliardGame.Instance().watchViewType === WatchViewType.View2D) {
      this.followDefault();
      return;
    }
    // 3D 视角
    switch (this.cameraType) {
      // 2D 视角 垂直俯视
      case CameraMoveType.Default:
        this.followDefault();
        break;
      // 跟随目标移动
      case CameraMoveType.FollowTarget:
        this.followTarget();
        break;
      // 固定点
      case CameraMoveType.FixedPointTarget:
        this.followFixedPoint();
        break;
      // 平滑移动
      case CameraMoveType.SmoothMoveTo:
        this.smoothMoveToTarget(deltaTime);
        break;
      // 击球时瞄准线的方向
      case CameraMoveType.AimPointDirection:
        this.aimPointDirection();
        break;
      // 击球时瞄准线的方向
      case CameraMoveType.StartGameScene:
        this.startGameScene(deltaTime);
        break;
      default:
        break;
    } 
  }
  
  private followDefault() {
    this.cameraNode.fov = 46;
    this.node.setPosition(this.oriCameraPos);
    this.node.lookAt(new Vec3(0, 0, 0), new Vec3(0, 0, -1));
  }
  private followFixedPoint() {
    this.cameraNode.fov = this.cameraFov;
    this.node.setPosition(this.initCameraPos);
    this.node.lookAt(this.targetPosition);
  }
  private followTarget() {
    if (!this.targetObject) { return; }
    this.cameraNode.fov = this.cameraFov;
    this.node.setPosition(this.targetObject.worldPosition.x + 25, this.oriCameraPos.y - 15, this.targetObject.worldPosition.z);
    this.node.lookAt(this.targetObject.worldPosition);
  }
  private aimPointDirection() {
    this.cameraNode.fov = this.cameraFov;
    this.node.setPosition(this.initCameraPos);
    this.node.lookAt(this.aimLookPoint);
  }
  private smoothMoveToTarget(dt: number) {
    this.cameraNode.fov = this.cameraFov;
    const toPos = this.fromPosition.lerp(this.targetPosition, this.smoothMoveRate);
    const toLook = this.fromLookPosition.lerp(this.targetLookPosition, this.smoothMoveRate);

    this.node.setPosition(toPos);
    this.node.lookAt(toLook);
  }
  private startGameScene(dt: number) {
    this.cameraNode.fov = this.cameraFov;
    const toPos = this.fromPosition.lerp(this.targetPosition, 0.025);
    const toLook = this.fromLookPosition.lerp(this.targetLookPosition, 0.025);

    this.node.setPosition(toPos);
    this.node.lookAt(toLook);
  }
  private updateFollowTarget(name: string, target: Node) {
    this.cameraNode.fov = this.cameraFov;
    this.cameraType = CameraMoveType.FollowTarget;
    this.targetObject = target;
  }
  private updateAimPointDirection(name: string, param: { 
    from: Vec3, to: Vec3, lookPoint: Vec3, mbPos: Vec3, aimFlag: number, cameraHeight: number }) {
    this.cameraType = CameraMoveType.AimPointDirection;
    if (param.aimFlag === 2) { // 最佳击球视角【进洞的视角】
      const behindPoint = this.calcuInnerPoint(param.from, param.to, param.mbPos);
      this.aimLookPoint.set(param.from.x, TableAtrribute.DeskTopAxisY, param.from.z);
      this.initCameraPos.set(behindPoint);

      this.cameraFov = 50;
    } else if (param.aimFlag === 1) { // 球杆瞄准视角【正常瞄准视角】
      const behindPoint = this.calcuQiuGanBackViewPoint(param.from, param.to);
      this.aimLookPoint.set(param.lookPoint);
      this.initCameraPos.set(behindPoint);
      this.cameraFov = 60;
    } else { // 0 观看最佳视角
      const behindPoint = this.calcuQiuGanBackPoint(param.from, param.to);
      this.aimLookPoint.set(param.lookPoint);
      this.initCameraPos.set(behindPoint);
      this.cameraFov = 60;
    }
    this.initCameraPos.y = TableAtrribute.DeskTopAxisY + param.cameraHeight * 4;
  }
  private updateSmoothMoveDirection(name: string, param: any) {
    this.ts = 0;
    this.cameraType = CameraMoveType.SmoothMoveTo;
    this.fromPosition.set(this.initCameraPos);
    this.fromLookPosition.set(this.aimLookPoint);

    const tot = this.getNearestPoint(this.initCameraPos);
    tot.y = TableAtrribute.DeskTopAxisY + 12;
    this.targetPosition.set(tot);
    this.targetLookPosition.set(0, TableAtrribute.DeskTopAxisY, 0);
    this.cameraFov = 50;
  }
  private updateFixedPoint(name: string, pt: Vec3) {
    this.cameraFov = 46;
    this.cameraType = CameraMoveType.FixedPointTarget;
    this.targetPosition = pt;
  }
  private calcuInnerPoint(from: Vec3, to: Vec3, mbPos: Vec3) {
    const dist = distanceLength(from, mbPos) + 12;
    const dir = Vector3.sub(to, from); dir.normalize();
    const maxx = TableAtrribute.DeskHalfAxisXLength + 15;
    const maxz = TableAtrribute.DeskHalfAxisZLength + 10;
    for (var n = 20; n < 50; n +=1) {
      const dx = from.x - n * dir.x;
      const dz = from.z - n * dir.z;
      if (Math.abs(dx) > maxx || Math.abs(dz) > maxz) {
        return new Vec3(dx, 0, dz);
      }
      const d = Math.sqrt((from.x - dx) * (from.x - dx) + (from.z - dz) * (from.z - dz));
      if (d >= dist) {
        return new Vec3(dx, 0, dz);
      }
    }
    return new Vec3(from.x - 20 * dir.x, 0, from.z - 20 * dir.z);
  }
  // 要到球桌之外，并且还需要漏出球杆
  private calcuQiuGanBackPoint(from: Vec3, to: Vec3) {
    const dir = Vector3.sub(to, from); dir.normalize();
    const maxx = TableAtrribute.DeskHalfAxisXLength + 10;
    const maxz = TableAtrribute.DeskHalfAxisZLength + 8;
    for (var n = 5; n < 50; n +=1) {
      const dx = from.x - n * dir.x;
      const dz = from.z - n * dir.z;
      if (Math.abs(dx) > maxx || Math.abs(dz) > maxz) {
        return new Vec3(dx, 0, dz);
      }
    }
    return new Vec3(from.x - 10 * dir.x, 0, from.z - 10 * dir.z);
  }
  private calcuQiuGanBackViewPoint(from: Vec3, to: Vec3) {
    const dir = Vector3.sub(to, from); dir.normalize();
    return new Vec3(from.x - 15 * dir.x, 0, from.z - 15 * dir.z);
  }
  private getNearestPoint(pt: Vec3) {
    const ptList = [
      {x: TableAtrribute.DeskHalfAxisXLength + 16, y: 0, z: 0 },
      {x: -TableAtrribute.DeskHalfAxisXLength - 16, y: 0, z: 0 },
      {x: 0, y: 0, z: TableAtrribute.DeskHalfAxisZLength + 20 },
      {x: 0, y: 0, z: -TableAtrribute.DeskHalfAxisZLength - 20 },
    ];
    var mind = 1000000000;
    var res = new Vec3();
    for (const p of ptList) {
      const dist = (p.x - pt.x) * (p.x - pt.x) + (p.z - pt.z) * (p.z - pt.z);
      if (dist < mind) {
        mind = dist;
        res.set(p.x, p.y, p.z);
      }
    }
    return res;
  }
}

